package system;

import java.io.*;
import java.util.*;

import record.*;
import request.*;

/**
 * @author Sean Freitag
 * A simple, test Database implementation
 * Final implementation will use a database connector to receive and send queries to MySQL
 */
//suppresses casting warnings
@SuppressWarnings("unchecked")
public class LocalData implements Database{
	
	/**
	 * Table of users
	 */
	private static HashMap<String, UserRecord> userTable = null;
	
	/**
	 * Table of projects
	 */
	private static HashMap<String, LinkedList<ProjectRecord>> projectsTable = null;
	
	/**
	 * Database file
	 */
	private static File file = new File("objects.file");
	
	/**
	 * 
	 */
	private static RecordFactory factory = new RecordFactory();
	
	static {

		try {

			FileInputStream obj = new FileInputStream(file);
			ObjectInputStream in = new ObjectInputStream(obj);
			
			userTable = (HashMap<String, UserRecord>) in.readObject();
			projectsTable = (HashMap<String, LinkedList<ProjectRecord>>) in.readObject();
			
			in.close();
			
		} catch (Exception e) {
			//if an error occurs, erase and re-make tables
			//Typically this happens when a class has a new serialization
			//Also, this can happen when file does not exist
			userTable = new HashMap<String, UserRecord>();
			projectsTable = new HashMap<String, LinkedList<ProjectRecord>>();
			save();
		}
		
	}
	
	/**
	 * Saves the tables to a file
	 * 
	 * This happens after each write to a table
	 * 
	 * It is not intended to be efficient, just effective
	 * 
	 * Efficiency will happen when a MySQL database is implemented
	 */
	private static synchronized void save(){
		
		try {

			FileOutputStream obj = new FileOutputStream(file);
			ObjectOutputStream out = new ObjectOutputStream(obj);

			out.writeObject(userTable);
			out.writeObject(projectsTable);
			
			out.close();
		} catch (Exception e) {
			e.printStackTrace();
		}

	}
	
	/**
	 * Adds a new user to the database
	 * @param request
	 * NewUserRequest received from client
	 * @return
	 * UserRecord for the new user
	 */
	public synchronized Record addUser(NewUserRequest request){
		
		if(request == null)
			return null;
		
		if(userTable.containsKey(request.getEmailAddress())){
			return null;
		}else{
			//UserRecord record = new UserRecord(request);
			UserRecord record = (UserRecord) factory.getRecord(request);
			userTable.put(request.getEmailAddress(), record);
			projectsTable.put(request.getEmailAddress(), new LinkedList<ProjectRecord>());
			save();
			return record;
		}
	}
	
	/**
	 * Creates a new user session
	 * @param request
	 * NewSessionRequest from client
	 * @return
	 * UserRecord for the already created user
	 */
	public synchronized UserRecord newSession(NewSessionRequest request){
		
		if(request == null)
			return null;
		
		UserRecord record = userTable.get(request.getEmail());
		
		if(record == null)
			return null;
			
		if(request.getPassword().equals(record.getPassword())){
			save();
			return record;
		}else{
			return null;
		}
		
	}
	
	/**
	 * Creates a new ProjectRecord
	 * @param username
	 * Name of the user, provided by the user during an already established session
	 * @param request
	 * NewProjectRequest from client
	 * @return
	 * ProjectRecord for the new project
	 */
	public synchronized ProjectRecord newProject(String username, NewProjectRequest request){
		
		if(username == null || request == null)
			return null;
		
		ProjectRecord record = (ProjectRecord) factory.getRecord(request);
		
		if(record == null)
			return null;
		
		LinkedList<ProjectRecord> records = projectsTable.get(username);
		if(records == null){
			projectsTable.put(username, new LinkedList<ProjectRecord>());
			records = projectsTable.get(username);
		}
			
		if(!records.contains(record)){
			records.add(record);
			save();
			return record;
		}else{
			return null;
		}
			
	}

	/**
	 * Retrieves an already created ProjectRecord
	 * @param username
	 * Name of the user, provided by the user during an already established session
	 * @param request
	 * ProjectRecordRequest from client
	 * @return
	 * ProjectRecord that was requested
	 */
	public synchronized ProjectRecord getProject(String username, ProjectRecordRequest request) {
		
		if(username == null || request == null)
			return null;
		
		LinkedList<ProjectRecord> records = projectsTable.get(username);
		
		if(records == null)
			return null;
		
		Iterator<ProjectRecord> iterator = records.iterator();
		ProjectRecord record;
		while(iterator.hasNext()){
			record = iterator.next();
			if(request.getProjectName().equals(record.getName())){
				return record;
			}
		}
		return null;
	}
	
	/**
	 * Returns all projects for a given user
	 * @param request 
	 * @param username
	 * Name of the user, provided by the user during an already established session
	 * @return
	 * AllProjectsRecord containing the projects
	 */
	public synchronized AllProjectsRecord getAllProjects(AllProjectsRequest request){
		
		if(request.getUser() == null)
			return null;
		
		request.setProjects(projectsTable.get(request.getUser()));
		
		return (AllProjectsRecord) factory.getRecord(request);
	}

	/**
	 * Unimplemented means of changing the user's credentials
	 * @param user
	 * Name of the user, provided by the user during an already established session
	 * @param request
	 * ChangeCredentialsRequest for changing credentials
	 * @return
	 * UserRecord with changed credentials
	 */
	public synchronized UserRecord changeCredentials(String user, ChangeCredentialsRequest request) {
		UserRecord record = null;
		if(request.getPassword() != null)
		{
			record = (UserRecord)factory.getRecord(request);
			userTable.put(user, record);
		}
		save();
		return record;
	}

	/**
	 * Removes a project from the database
	 * @param username
	 * Name of the user, provided by the user during an already established session
	 * @param request
	 * RemoveProjectRequest for removing a project
	 * @return
	 * AllProjectsRecord after the removal
	 */
	public synchronized AllProjectsRecord removeProject(RemoveProjectRequest request) {
		
		if(request.getUser() == null || request == null)
			return null;
		
		LinkedList<ProjectRecord> records = projectsTable.get(request.getUser());
		
		if(records == null)
			return null;
		
		records.remove(request.getRecord());
		save();
		
		request.setProjects(projectsTable.get(request.getUser()));
		
		return (AllProjectsRecord) factory.getRecord(request);
	}

	/**
	 * Saves a Record submitted by the client
	 * @param user
	 * Name of the user, provided by the user during an already established session
	 * @param request
	 * SaveRequest containing record to be saved
	 * @return
	 * The record intending to be saved
	 */
	public synchronized Record save(String user, SaveRequest request) {
		if(user == null || request == null)
			return null;
		
		if(request.getRecord() instanceof ProjectRecord){
			LinkedList<ProjectRecord> records = projectsTable.get(user);
			
			if(records == null)
				return null;
			
			records.remove(request.getRecord());
			records.add((ProjectRecord)request.getRecord());
			
			return request.getRecord();
		}
		save();
		return null;
	}
	
}
